package cn.edu.swpu.cins.u_chain.frontend.service.user.Impl;

import cn.edu.swpu.cins.u_chain.Exception.user.UserException;
import cn.edu.swpu.cins.u_chain.async.EventModel;
import cn.edu.swpu.cins.u_chain.async.EventProducer;
import cn.edu.swpu.cins.u_chain.async.EventType;
import cn.edu.swpu.cins.u_chain.frontend.dao.user.UserDao;
import cn.edu.swpu.cins.u_chain.frontend.entity.dto.project.ProViewDetail;
import cn.edu.swpu.cins.u_chain.frontend.entity.dto.user.*;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.City;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.County;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.Province;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.user.BasicInfo;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.user.ContactInfoDB;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.user.LocationInfo;
import cn.edu.swpu.cins.u_chain.frontend.entity.persistence.user.UserPublic;
import cn.edu.swpu.cins.u_chain.frontend.service.user.UserService;
import cn.edu.swpu.cins.u_chain.utils.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    @Autowired
    private JedisAdapter jedisAdapter;
    @Autowired
    private PasswordService passwordService;
    @Autowired
    private UUIDService uuidService;
    @Autowired
    private EventProducer eventProducer;
    @Autowired
    private UploadFile uploadFile;
    /**
     * 使用密码登录
     * @param userLogin
     * @param VCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public UserSignResult loginByPwd(UserLogin userLogin, String VCode) {
        //验证登录验证码
        String code = userLogin.getVCode();
        String redisVCodeKey = VCode;
        UserPublic user = userDao.selectUserByPhone(userLogin.getPhoneNum());
        if (user == null) {
            throw new UserException("用户不存在");
        }
        if (!passwordService.match(userLogin.getPwd(), user.getPwd())) {
            throw new UserException("密码错误");
        }
//        if (!jedisAdapter.exists(redisVCodeKey)) {
//            //throw new UserException("请重新获取验证码");
//        }
//        if (jedisAdapter.get(redisVCodeKey).equals(code)) {
//            //throw new UserException("验证码错误");
//        }
        //生成token
        String tokenKey = uuidService.getUUID();
        String token = passwordService.encode(tokenKey);
        String phoneNum = user.getPhoneNum();
        jedisAdapter.setex(tokenKey, 86400, tokenKey);
        return new UserSignResult(token, user.getUserId(), user.getRoleId(), phoneNum);
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public UserSignResult loginByPhone(UserLogin userLogin) {
        //逻辑基本上是从redis中取出验证码 和收到的验证码匹配
        UserPublic user = userDao.selectUserByPhone(userLogin.getPhoneNum());
        if (user == null) {
            throw new UserException("用户不存在");
        }

        String key = RedisKey.getuChainLogin(userLogin.getPhoneNum());
        if (!jedisAdapter.exists(key)) {
            //throw new UserException("重新获取验证码");
        }
//        if (!userLogin.getVCode().equals(jedisAdapter.get(key))) {
//            throw new UserException("验证码错误");
//        }
        //生成token
        String tokenKey = uuidService.getUUID();
        String token = passwordService.encode(tokenKey);
        String phoneNum = user.getPhoneNum();
        jedisAdapter.setex(tokenKey, 86400, tokenKey);
        return new UserSignResult(token, user.getUserId(), user.getRoleId(), phoneNum);
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public int register(UserPublic userPublic) {
        //验证验证码
        String key = RedisKey.getuChainRegister(userPublic.getPhoneNum());
//        if (!jedisAdapter.exists(key)) {
//            throw new UserException("重新获取验证码");
//        }
//        if (!jedisAdapter.get(key).equals(userPublic.getCode())) {
//            //throw new UserException("验证码错误");
//        }
        UserPublic user = userDao.selectUserByPhone(userPublic.getPhoneNum());
        if (user != null) {
            throw new UserException("手机号已经被注册");
        }
        userPublic.setPwd(passwordService.encode(userPublic.getPwd()));
        userPublic.setCreateDate(new Date().getTime());

        return userDao.addUser(userPublic);
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public int updatePwd(UpdatePwd updatePwd) {
        //修改密码 需要旧密码与新密码
        UserPublic user = userDao.selectUserByPwd(updatePwd.getUserId());
        if (!passwordService.match(updatePwd.getOldPwd(), user.getPwd())) {
            throw new UserException("原密码错误");
        }
        String pwd = passwordService.encode(updatePwd.getNewOld());
        return userDao.updatePwd(updatePwd.getUserId(), pwd);
    }

    //通过手机找回密码
    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public int findPwd(ForgetPwd forgetPwd) {
        UserPublic user = userDao.selectUserByPhone(forgetPwd.getPhoneNum());
        if (user == null) {
            throw new UserException("没有该用户");
        }
        String key = RedisKey.getuChainFindPwd(forgetPwd.getPhoneNum());
        if (!jedisAdapter.exists(key)) {
            throw new UserException("重新获取验证码");
        }
        if (!forgetPwd.getPhoneCode().equals(jedisAdapter.get(key))) {
            throw new UserException("验证码错误");
        }
        String pwd = passwordService.encode(forgetPwd.getPwd());
        return userDao.updatePwd(user.getUserId(), pwd);

    }

    /**
     * 添加详细联系信息
     * @param contactInfoView
     * @return
     */
    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public int addContactInfo(ContactInfoView contactInfoView,int userId) {
        ContactInfoDB contactInfoDB = getContactInfoDB(contactInfoView, userId);
        return userDao.addContactInfo(contactInfoDB);
    }

    @Override
    public int addBasicInfo(BasicInfo basicInfo, int userId) {
        basicInfo.setUserId(userId);
        return userDao.addBasicInfo(basicInfo);

    }

    @Override
    public BasicInfo getBasicInfo(int userId) {
        return userDao.selectBasicInfo(userId);
    }

    @Override
    public ContactInfoDetail getContactInfo(int userId) {
        return userDao.selectContactInfo(userId);
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class,UserException.class,SQLException.class})
    public void checkMail(UpdateBindMail updateBindMail) {
        int userId = updateBindMail.getUserId();
        String mail;
        String status = updateBindMail.getStatus();
        if (status.equals("changeMail")){
            mail = userDao.selectBasicInfo(userId).getMail();
        }else {
            mail = updateBindMail.getMail();
        }
        addEventQueue(userId, mail, status);
    }


    @Override
    public int updateBindMail(UpdateBindMail updateBindMail) {
        int userId = updateBindMail.getUserId();
        String mail = updateBindMail.getMail();
        String verifyCode = updateBindMail.getVerifyCode();
        String key = RedisKey.getuChainMailConfirm(userId, mail);
        String code = jedisAdapter.get(key);
        if (!jedisAdapter.exists(key)) {
            throw new UserException("请重新获取验证码");
        }
        if (!verifyCode.equals(code)) {
            throw new UserException("验证码错误");
        }
        return userDao.updateMail(userId, mail);
    }

    @Override
    @Transactional(rollbackFor = {UserException.class,SQLException.class,IOException.class,RuntimeException.class})
    public int uploadImage(int userId, MultipartFile image) throws IOException {
        String path = uploadFile.upload(image, userId, "image");
        return userDao.updateImage(userId, path);

    }

    @Override
    public int updateBasicInfo(BasicInfo basicInfo) {
        return userDao.updateBasic(basicInfo);
    }

    /**
     * 这里要不要删除原来的locationId呢？
     * @param contactInfoView
     * @return
     */
    @Override
    public int updateContact(ContactInfoView contactInfoView) {
        int userId = contactInfoView.getUserId();
        ContactInfoDB contactInfoDB = getContactInfoDB(contactInfoView, userId);
        return userDao.updateContactInfo(contactInfoDB);
    }

    /**
     * 缓存有就直接从缓存取 缓存没有就从数据库取
     * @return
     */
    @Override
    public LocationView getLocation() {
        LocationView locationView;
        String locationKey = RedisKey.getLocationAll();
        if (jedisAdapter.exists(locationKey)) {
            System.out.println("从缓存中获取");
            String value = jedisAdapter.get(locationKey);
            locationView = JSON.parseObject(value, LocationView.class);
        } else {//缓存中不存在
            locationView = getLocationView();
            System.out.println("从数据库中获取");
            //加入缓存
            String json = JSONObject.toJSONString(locationView);
            String key = locationKey;
            jedisAdapter.setKey(key, json);
        }
        return locationView;
    }



    public LocationView getLocationView() {
        List<Province> provinces = userDao.selectAllProvince();
        LocationView locationView = new LocationView(getProvinceView(provinces));
        return locationView;
    }

    public List<ProvinceView> getProvinceView(List<Province> provinces) {
        List<ProvinceView> provinceViews = new ArrayList<>();
        provinces.stream().forEach(province -> {
            String provinceId = province.getProvinceId();
            String provinceName = province.getProvinceName();
            List<City> cities = userDao.selectAllCityByProvinceId(provinceId);
            List<CityView> cityViews = getCityView(cities);
            //构造provinceView
            ProvinceView provinceView = new ProvinceView(provinceId, provinceName, cityViews);
            provinceViews.add(provinceView);
        });
        return provinceViews;
    }

    public List<CityView> getCityView(List<City> cities) {
        List<CityView> cityViews = new ArrayList<>();
        cities.stream().forEach(city -> {
            String cityId = city.getCityId();
            String cityName = city.getCityName();
            List<County> counties = userDao.selectAllCountyByCityId(cityId);
            CityView cityView = new CityView(cityId, cityName, counties);
            cityViews.add(cityView);
        });
        return cityViews;
    }

    public ContactInfoDB getContactInfoDB(ContactInfoView contactInfoView,int userId) {
        ContactInfoDB contactInfoDB = new ContactInfoDB(contactInfoView);
        LocationInfo locationInfo = new LocationInfo(contactInfoView);
        userDao.addLocationInfo(locationInfo);
        int locationId = locationInfo.getLocationId();
        contactInfoDB.setLocationId(locationId);
        contactInfoDB.setUserId(userId);
        return contactInfoDB;
    }

    public void addEventQueue(int userId, String mail, String status) {
        eventProducer.fireEvent(new EventModel(EventType.MAIL)
                .setExts("status", status)
                .setExts("userId", String.valueOf(userId))
                .setExts("mail",mail));
    }
}
